﻿using System.Collections.Generic;
using System.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a catalog item specification.
    /// </summary>
    /// <typeparam name="TSpec">The concrete specification type.</typeparam>
    public abstract class CreateCatalogItemSpec<TSpec> : CreateAuditable<TSpec>,
        ISecuredOperation, IApiOperation
        where TSpec : CatalogItemSpecEntity
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public virtual IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var item = uow.Execute(new GetCatalogItem
                        {
                            EntityID = ItemID
                        }).Result;
                        if(item == null)
                            return false;
                        return item.Supplier.Managers
                            .Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the item identifier.
        /// </summary>
        public int ItemID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the SKU.
        /// </summary>
        public string SKU
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CatalogItemSpecFlags CommonFlags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, TSpec entity)
        {
            base.Construct(uow, entity);

            entity.Item = uow.Execute(new GetCatalogItem
            {
                ThrowsException = true,
                EntityID = ItemID
            }).Result;
            entity.SKU = StringHelper.EnsureNotNull(SKU).TrimToLen(100);
            entity.CommonFlags = CommonFlags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the catalog item specification.
    /// </summary>
    /// <typeparam name="TSpec">The concrete specification type.</typeparam>
    public abstract class UpdateCatalogItemSpec<TSpec> : UpdateAuditable<TSpec>,
        ISecuredOperation, IApiOperation
        where TSpec : CatalogItemSpecEntity
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public virtual IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var translation = uow.Execute(new GetCatalogItemTranslation
                        {
                            EntityID = EntityID
                        }).Result;
                        if(translation == null)
                            return false;
                        return translation.Holder.Supplier
                            .Managers.Contains(manager);
                    });
            }
        }

        /// <summary>
        /// Gets or sets the SKU.
        /// </summary>
        public string SKU
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public CatalogItemSpecFlags CommonFlags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, TSpec entity)
        {
            base.Revise(uow, entity);

            entity.SKU = StringHelper.EnsureNotNull(SKU).TrimToLen(100);
            entity.CommonFlags = CommonFlags;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the catalog item specification.
    /// </summary>
    public class DeleteCatalogItemSpec : DeleteEntity<CatalogItemSpecEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("CTLG");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var manager = ctx.CatalogManager(uow);
                        if(manager == null || !manager.Flags.HasFlag(CatalogManagerFlags.Active))
                            return false;
                        var translation = uow.Execute(new GetCatalogItemTranslation
                        {
                            EntityID = EntityID
                        }).Result;
                        if(translation == null)
                            return false;
                        return translation.Holder.Supplier
                            .Managers.Contains(manager);
                    });
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the catalog item specification.
    /// </summary>
    public class GetCatalogItemSpec : GetEntity<CatalogItemSpecEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over catalog item specification collection.
    /// </summary>
    public class SearchCatalogItemSpecifications : SearchOperation<CatalogItemSpecEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchCatalogItemSpecifications()
        {
            SortDir = SortDirection.Ascending;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the item identifier.
        /// </summary>
        public int? ItemID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public CatalogItemSpecFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<CatalogItemSpecEntity> Build(IUnitOfWork uow, IQueryable<CatalogItemSpecEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.CommonFlags, (int)FlagMask);
            if(ItemID.HasValue)
                query = query.Where(x => x.Item.EntityID == ItemID.Value);
            query = query.OrderBy(x => x.EntityID, SortDir);
            return query;
        }

        #endregion
    }

    #endregion
}
